package avicit.im.lantop.basic.libstructuredefine.action.service.impl;

import avicit.cbb.techclass.tabledefine.commtablemanager.domain.CbbTabColumns;
import avicit.cbb.techclass.tabledefine.commtablemanager.domain.CbbTabNode;
import avicit.cbb.techclass.tabledefine.commtablemanager.domain.CbbTables;
import avicit.im.lantop.basic.archiveclassmanager.domain.LantopArchiveClass;
import avicit.im.lantop.basic.libstructuredefine.MultiTree;
import avicit.im.lantop.basic.libstructuredefine.TreeBuilder;
import avicit.im.lantop.basic.libstructuredefine.cbbtabexceptioncolumns.dto.CbbTabExceptionColumnsDTO;
import avicit.im.lantop.basic.libstructuredefine.cbbtabexceptioncolumns.service.CbbTabExceptionColumnsService;
import avicit.im.lantop.basic.libstructuredefine.dao.LantopArchiveClassDao2;
import avicit.platform6.api.sysuser.dto.SysOrg;
import avicit.platform6.commons.utils.ComUtil;
import avicit.platform6.commons.utils.PojoUtil;
import avicit.platform6.core.properties.PlatformConstant;
import com.google.common.base.CaseFormat;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.converters.BigDecimalConverter;
import org.apache.commons.beanutils.converters.DateConverter;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @author emorabu
 * @version 1.0
 * @date 2020/11/20
 */
@Service
public class LantopArchiveClassService2 {
    private static final Logger LOGGER = LoggerFactory.getLogger(LantopArchiveClassService2.class);
    @Autowired
    LantopArchiveClassDao2 lantopArchiveClassDao2;
    @Autowired
    CbbTabExceptionColumnsService cbbTabExceptionColumnsService;

    public List<MultiTree> buildAllArchiveClassTree(List<LantopArchiveClass> archiveClasses){
        List<MultiTree> treeList = new ArrayList<>(archiveClasses.size());
        MultiTree multiTree = null;
        for (LantopArchiveClass archiveClass : archiveClasses) {
            multiTree = new MultiTree<LantopArchiveClass>(archiveClass.getId(),archiveClass.getParentId(), archiveClass.getArchiveClassNodeName(), archiveClass);
            treeList.add(multiTree);
        }
        return TreeBuilder.buildListToTree(treeList);
    }

    /**
     * 构造节点标识-节点对象的map
     * @param allList
     * @param
     * @param
     * @return
     */
    public Map[] buildDataMap(List<MultiTree> allList){
        Map<String,LantopArchiveClass> fullPathToBeanMap = new HashMap<>();
        Map<String,String> nodeIdentityToFullPathMap = new HashMap<>();
        List<MultiTree> currentList = allList;
        putToNodeIdentityMap(currentList, fullPathToBeanMap, nodeIdentityToFullPathMap);
        return new Map[]{fullPathToBeanMap, nodeIdentityToFullPathMap};
    }

    private void putToNodeIdentityMap(List<MultiTree> currentList, Map<String, LantopArchiveClass> fullPathToBeanMap, Map<String,String> nodeIdentityToFullPathMap){
        if(CollectionUtils.isEmpty(currentList)){
            return;
        }
        String nodeFullPath;
        for (MultiTree multiTree : currentList) {
            nodeFullPath = getNodeFullPath(multiTree);
            fullPathToBeanMap.put(nodeFullPath, (LantopArchiveClass) multiTree.t);
            nodeIdentityToFullPathMap.put(getNodeIdentity((LantopArchiveClass) multiTree.t), nodeFullPath);
            putToNodeIdentityMap(multiTree.getChildren(), fullPathToBeanMap, nodeIdentityToFullPathMap);
        }
    }


    public String getNodeIdentity(LantopArchiveClass element){
        StringBuilder sb=new StringBuilder();
        String delimiter="-\u202A-";
        sb.append(element.getArchiveClassNodeName()).append(delimiter)
                .append(element.getArchiveClassNodeCode()).append(delimiter)
                .append(element.getOrgId()).append(delimiter)
                .append(element.getArchiveType())/*.append(delimiter)
                .append(element.getNodeSortNumber())*/;
        return sb.toString();
    }

    /**
     * 获取节点全路径
     * @return
     */
    public String getNodeFullPath(MultiTree multiTree) {
        StringBuilder sb=new StringBuilder();
        String delimiter="|\u202A|";
        getParentNodeFullPath(multiTree,sb,delimiter);
        //LOGGER.info(sb.toString());
        return sb.toString();
    }

    /**
     * 由于根节点和组织名称不一定一样, 这里使用组织id替代根
     * 根据父id获取所有父节点
     */
    public void getParentNodeFullPath(MultiTree multiTree, StringBuilder sb, String delimiter){
        if(multiTree == null){
            return;
        }
        LantopArchiveClass t = (LantopArchiveClass) multiTree.t;
        if("ROOT".equals(t.getParentId())){
            sb.insert(0, t.getOrgId());
            return;
        }
        sb.insert(0, getNodeIdentity(t));
        sb.insert(0, delimiter);
        getParentNodeFullPath(multiTree.getParent(), sb, delimiter);
    }


    private static AtomicBoolean lock = new AtomicBoolean(false);
    public void syncToSubOrg(String orgId, List<SysOrg> subOrgs, String nodeId) throws Exception{
        List<String> validOrgIds = new ArrayList<>(subOrgs.size()+1);
        for (SysOrg subOrg : subOrgs) {
            validOrgIds.add(subOrg.getId());
        }
        validOrgIds.add(orgId);

        try {
            while(!lock.compareAndSet(false, true)){
                Thread.sleep(2000);
            }
            List<LantopArchiveClass> allArchiveClassInLibraryStructure = lantopArchiveClassDao2.getAllArchiveClassInLibraryStructure(validOrgIds);
            List<MultiTree> treeList = buildAllArchiveClassTree(allArchiveClassInLibraryStructure);

            Map[] dataMap = buildDataMap(treeList);
            Map<String,LantopArchiveClass> fullPathToBeanMap = dataMap[0];
            Map<String,String> nodeIdentityToFullPathMap = dataMap[1];
          //  LOGGER.info("fullPathToBeanMap: "+fullPathToBeanMap.size());

            List<LantopArchiveClass> selectedArchiveClasses = lantopArchiveClassDao2.getRecursiveLantopArchiveClassById(nodeId);
            for (SysOrg subOrg : subOrgs) {
    //            LOGGER.info(subOrg.toString());
                for (LantopArchiveClass srcLantopArchiveClass : selectedArchiveClasses) {
                    createLantopArchiveClass(fullPathToBeanMap, nodeIdentityToFullPathMap, srcLantopArchiveClass, subOrg.getId());
                }
    //            break;// TODO 提交时去掉
            }

            syncCbbTablesToSubOrgs(nodeId, orgId, subOrgs, fullPathToBeanMap, nodeIdentityToFullPathMap);
        }catch (Exception e){
            LOGGER.info(e.getMessage());
            throw e;
        }finally {
            lock.set(false);
        }
    }

    public void createLantopArchiveClass(Map<String, LantopArchiveClass> fullPathToBeanMap, Map<String, String> nodeIdentityToFullPathMap, LantopArchiveClass srcLantopArchiveClass, String subOrgId){
        String nodeIdentity = getNodeIdentity(srcLantopArchiveClass);
        String srcFullPath = nodeIdentityToFullPathMap.get(nodeIdentity);
        String destFullPath = srcFullPath.replace(srcLantopArchiveClass.getOrgId(), subOrgId); // 替换为子组织的id
        LantopArchiveClass destLantopArchiveClass = fullPathToBeanMap.get(destFullPath);
        if(destLantopArchiveClass != null){ // 存在, 无需创建
            return;
        }
        List<LantopArchiveClass> destLantopArchiveClassList = new ArrayList<>(); // 记录新建的节点
        List<String> destFullPathList = new ArrayList<>(); // 对应新建节点的 fullPath
        destLantopArchiveClassList.add(deeptClone(srcLantopArchiveClass)); // 添加当前原节点的拷贝
        destFullPathList.add(destFullPath);
        String delimiter="|\u202A|";
        String parentId = null;
        while(destFullPath.indexOf(delimiter)!=-1){
            destFullPath = destFullPath.substring(0, destFullPath.lastIndexOf(delimiter));
            srcFullPath = srcFullPath.substring(0, srcFullPath.lastIndexOf(delimiter));
            destLantopArchiveClass = fullPathToBeanMap.get(destFullPath);
            if(destLantopArchiveClass == null){
                destLantopArchiveClassList.add(deeptClone(fullPathToBeanMap.get(srcFullPath))); // fullpath 由长变短, 即子元素在前, 父元素在后
                destFullPathList.add(destFullPath);
            }else{
                parentId = destLantopArchiveClass.getId();
                break;
            }
        }
        if(parentId!=null){
            destLantopArchiveClass = destLantopArchiveClassList.get(destLantopArchiveClassList.size()-1); // 最后一个元素, 是 fullpath 最短且不存在的元素.
            destLantopArchiveClass.setId(ComUtil.getId());
            destLantopArchiveClass.setParentId(parentId);
            destLantopArchiveClass.setOrgId(subOrgId);
            // 维护到上面两个map中
            destFullPath = destFullPathList.get(destFullPathList.size()-1); //对应最后一个fullpath
            nodeIdentityToFullPathMap.put(getNodeIdentity(destLantopArchiveClass), destFullPath);
            fullPathToBeanMap.put(destFullPath, destLantopArchiveClass);
            destLantopArchiveClass.setArchiveClassNodePath(buildArchiveClassNodePath(destFullPath, fullPathToBeanMap));
            lantopArchiveClassDao2.saveLantopArchiveClass(destLantopArchiveClass);
        }

        // 从后向前遍历 list, 依次设置 id 和pid
        for(int i = destLantopArchiveClassList.size()-2; i>=0 ;i--){
            destLantopArchiveClass = destLantopArchiveClassList.get(i);
            destLantopArchiveClass.setId(ComUtil.getId());
            destLantopArchiveClass.setParentId(destLantopArchiveClassList.get(i+1).getId());
            destLantopArchiveClass.setOrgId(subOrgId);
            destFullPath = destFullPathList.get(i);

            nodeIdentityToFullPathMap.put(getNodeIdentity(destLantopArchiveClass), destFullPath);
            fullPathToBeanMap.put(destFullPath, destLantopArchiveClass);
            destLantopArchiveClass.setArchiveClassNodePath(buildArchiveClassNodePath(destFullPath, fullPathToBeanMap));
            lantopArchiveClassDao2.saveLantopArchiveClass(destLantopArchiveClass);
        }
    }

    public String buildArchiveClassNodePath(String fullPath, Map<String, LantopArchiveClass> fullPathToBeanMap){
        StringBuilder archiveClassNodePath = new StringBuilder();
        int index ;
        while((index = fullPath.lastIndexOf("|\u202A|"))!=-1){
            archiveClassNodePath.insert(0, fullPathToBeanMap.get(fullPath).getId());
            archiveClassNodePath.insert(0,"/");
            fullPath = fullPath.substring(0,index);
        }

        if(archiveClassNodePath.length()<=1){
            return fullPath;
        }else {
            return archiveClassNodePath.substring(1);
        }
    }

    public void syncCbbTablesToSubOrgs(String nodeId, String orgId, List<SysOrg> subOrgs, Map<String, LantopArchiveClass> fullPathToBeanMap, Map<String, String> nodeIdentityToFullPathMap) throws Exception {
        // 查询到 cbbtable节点, 及其父节点archiveclass, 对于每个 父节点->nodeidentity->fullpath
        // 替换为子组织的 fullpath -> 对象 -> id ,
        // cbbtable -> 每个子组织的archiveclass类中的id, 数据库关联查询, cbbtables, cbb_tab_node 对象,
        Map<String, String> cbbTablesIdToFullPathMap = buildCbbTablesIdToFullPathMap(nodeId, nodeIdentityToFullPathMap);

        //final List<LantopArchiveClass> cbbTablesParentArchiveClasses = lantopArchiveClassDao2.getCbbTablesParentArchiveClass(nodeId);
        String srcFullPath;
        String destFullPath;
        LantopArchiveClass destLantopArchiveClass;
        String cbbTableParentId;

        Set<String> cbbTablesIds = cbbTablesIdToFullPathMap.keySet();
        if(CollectionUtils.isEmpty(cbbTablesIds)){
            return;
        }
        List<CbbTables> cbbTables = lantopArchiveClassDao2.getCbbTablesByIds(cbbTablesIds);
        List<CbbTabNode> cbbTabNodes = lantopArchiveClassDao2.getCbbTabNodesByTableIds(cbbTablesIds);


        String subOrgId;
        ArrayList<String> subOrgIds = new ArrayList<>();
        for(SysOrg subOrg: subOrgs) {
            subOrgId = subOrg.getId();
            subOrgIds.add(subOrgId);
//            break;// TODO 提交时去掉
        }
        CbbTables srcCbbTables;
        List<String> validOrgIds;
        int index = 0;
        CbbTables cbbTablesItem;
        CbbTabNode cbbTabNode;
        List<String> restOrgIds;
        int numOfCbbTableName;
        for(String cbbTablesId: cbbTablesIds) {
            srcCbbTables = cbbTables.get(index);
            srcFullPath = cbbTablesIdToFullPathMap.get(cbbTablesId);
            LantopArchiveClass srcLantopArchiveClass = fullPathToBeanMap.get(srcFullPath);
            String srcArchiveClassNodeCode = srcLantopArchiveClass.getArchiveClassNodeCode();
            validOrgIds = lantopArchiveClassDao2.getOrgIdsByCbbTables(srcCbbTables, subOrgIds, srcArchiveClassNodeCode);
            restOrgIds = getRestElement(subOrgIds, validOrgIds);
            numOfCbbTableName = lantopArchiveClassDao2.getNextNumOfCbbTableName(srcCbbTables.getTableName(), srcArchiveClassNodeCode);
            for (String restOrgId : restOrgIds) { // 要创建的
                destFullPath = srcFullPath.replace(orgId, restOrgId); // 替换为子组织的id
                destLantopArchiveClass = fullPathToBeanMap.get(destFullPath);
                if(destLantopArchiveClass==null){
                    createLantopArchiveClass(fullPathToBeanMap,nodeIdentityToFullPathMap, srcLantopArchiveClass, restOrgId);
                    destLantopArchiveClass = fullPathToBeanMap.get(destFullPath);
                    if(destLantopArchiveClass==null){
                        continue;
                    }
                }
                cbbTableParentId = destLantopArchiveClass.getId();

                cbbTablesItem = deeptClone(srcCbbTables);
                cbbTablesItem.setId(ComUtil.getId());
                cbbTablesItem.setOrgId(restOrgId);
                cbbTablesItem.setTableName(buildCbbTableName(cbbTablesItem.getTableName(), destLantopArchiveClass.getArchiveClassNodeCode(), numOfCbbTableName++));
                lantopArchiveClassDao2.saveCbbTables(cbbTablesItem);

                cbbTabNode = deeptClone(cbbTabNodes.get(index));
                cbbTabNode.setId(ComUtil.getId());
                cbbTabNode.setNodeId(cbbTableParentId);
                cbbTabNode.setTableId(cbbTablesItem.getId());
                cbbTabNode.setOrgId(restOrgId);
                lantopArchiveClassDao2.saveCbbTabNode(cbbTabNode);

                List<CbbTabColumns> cbbTabColumnsList = copyCbbTabColumns(srcCbbTables.getId(), cbbTablesItem.getId(), restOrgId);
                lantopArchiveClassDao2.createPhysicalTable(cbbTablesItem, cbbTabColumnsList);
            }
            index++;
            // 要同步的
            if(validOrgIds.size()>0){
                syncCbbTabColumns(cbbTablesId, validOrgIds);
            }
        }

    }

    public String buildCbbTableName(String srcTableName, String archiveClassNodeCode, int num){
        StringBuilder prefix = new StringBuilder("LANTOP").append("_").append(archiveClassNodeCode).append("_"); // LANTOP_JC_
        StringBuilder suffix = new StringBuilder(srcTableName.substring(srcTableName.lastIndexOf("_"))); // _AJJ
        return prefix.append(num).append(suffix).toString();
    }


    /**
     * 获取差集
     * @param all
     * @param sub
     * @return
     */
    public List<String> getRestElement(ArrayList<String> all, List<String> sub){
        List<String> temp = (List<String>) all.clone();
        temp.removeAll(sub);
        return temp;
    }
/*    public HashSet<String> getRestElement(Set<String> all, Set<String> sub){
        HashSet<String> temp = new HashSet<>(all);
        temp.removeAll(sub);
        return temp;
    }*/

    public List<CbbTabColumns> copyCbbTabColumns(String srcTableId, String destTableId, String orgId){
        List<CbbTabColumns> srcCbbTabColumnsList = lantopArchiveClassDao2.getCbbTabColumnsByTableId(srcTableId);
        CbbTabColumns destCbbTabColumns;
        List<CbbTabColumns> destCbbTabColumnsList = new ArrayList<>();
        for (CbbTabColumns srcCbbTabColumns : srcCbbTabColumnsList) {
            destCbbTabColumns = deeptClone(srcCbbTabColumns);
            destCbbTabColumns.setId(ComUtil.getId());
            destCbbTabColumns.setOrgId(orgId);
            destCbbTabColumns.setTableId(destTableId);
//                lantopArchiveClassDao2.saveCbbTabColumns(destCbbTabColumns);
            destCbbTabColumnsList.add(destCbbTabColumns);
//            LOGGER.info(destCbbTabColumns.getColLabel()+" "+destCbbTabColumns.getColName()+" "+destCbbTabColumns.getTableId());
        }
        lantopArchiveClassDao2.saveCbbTabColumnsList(destCbbTabColumnsList);
        return destCbbTabColumnsList;
    }

    public Map<Object,Object> syncCbbTabColumns(String srcTableId, List<String> orgIds) throws Exception {
        CbbTables srcCbbTables = lantopArchiveClassDao2.getCbbTablesById(srcTableId);
        Map<String, Map<String, Object>> srcCbbTabColumnsMap = lantopArchiveClassDao2.getCbbTabColumnsMapByTableId(srcTableId); // colname-cbbtabcolumn
        List<CbbTables> destCbbTablesList = lantopArchiveClassDao2.getCbbTablesListInOtherOrg(srcCbbTables, orgIds);

        String[] numberFields = {
                "COL_LENGTH",
                "COL_DECIMAL",
                "COL_ORDER",
                "ATTRIBUTE_11",
                /*
                "ATTRIBUTE_12",
                "ATTRIBUTE_13",
                "ATTRIBUTE_14",
                "ATTRIBUTE_15",*/
                "COL_COMPLEMENT_NUM",
        };
        String[] stringFields = {
            "COL_LABEL",

            "COL_TYPE",
            "COL_IS_SYS",
            "COL_IS_MUST",
            "COL_IS_VISIBLE",
            "COL_IS_SEARCH",
            "COL_IS_EDIT",
            "COL_IS_TAB_VISIBLE",
            "COL_IS_DETAIL",
            "COL_DROPDOWN_TYPE",
            "COL_GENE_METHOD",
            "COL_RULE_NAME",
            "COL_RULE_TITLE",
            "CUSTOM_PATH",
            "REMARK",

            "SYS_ID",
            "SECRET_LEVEL",
            "SYS_APPLICATION_ID",
           /* "ATTRIBUTE_01",
            "ATTRIBUTE_02",
            "ATTRIBUTE_03",
            "ATTRIBUTE_04",
            "ATTRIBUTE_05",
            "ATTRIBUTE_06",
            "ATTRIBUTE_07",
            "ATTRIBUTE_08",
            "ATTRIBUTE_09",
            "ATTRIBUTE_10",*/
            "COL_SHOW_FORMAT",
            "COL_GENE_METHOD_RULE",
            "COL_IS_DISPLAY",
            "COL_IS_COMPLEMENT",
            "COL_GENE_METHOD_RULE_TEXT",
        };
        HashMap<Object, Object> result = new HashMap<>();
        HashMap<Object, Object> syncInfo;
        Object subOrgId;
        List<Map<String,Object>> srcMoreThanDestList; // 源节点比目标节点多的列
        List<Map<String, Object>[]> destMoreThanSrcList; // 目标节点比源节点多的列
        List<Map<String, CbbTabExceptionColumnsDTO>> exceptionList = null; // 源节点和目标节点中都存在, 但其他属性不同的列

        Map<String, Map<String, Object>> destCbbTabColumnsMap;
        Map<String,CbbTabExceptionColumnsDTO> exceptionMap;  // 字段中文名-其他字段
        List<Map<String, Object>[]> overwriteColumnList; // 要覆盖的列, 包括字段中文名, 较长的长度
        List<Map<String, Object>[]> colLengthDiffList;
        List<CbbTabExceptionColumnsDTO> cbbTabExceptionColumnsDTOList;
        for (CbbTables destCbbTables : destCbbTablesList) { // 每个组织下的 CbbTables
            destCbbTabColumnsMap = lantopArchiveClassDao2.getCbbTabColumnsMapByTableId(destCbbTables.getId()); // 每个组织下的 colname-cbbtabcolumns

            srcMoreThanDestList = new ArrayList<>();
            destMoreThanSrcList = new ArrayList<>();
            exceptionList = new ArrayList<>();
            overwriteColumnList = new ArrayList<>();
            colLengthDiffList = new ArrayList<>();
            cbbTabExceptionColumnsDTOList = new ArrayList<>();
            for (String colName : destCbbTabColumnsMap.keySet()) { // 遍历目标列
               Map<String, Object> srcCbbTabColumnsItem = srcCbbTabColumnsMap.get(colName);
               Map<String, Object> destCbbTabColumnsItem = destCbbTabColumnsMap.get(colName);
               if(srcCbbTabColumnsItem == null){ // 目标节点有而源节点没有, 如果多出来的字段在表里没有值, 就删除该列,否则保留
                    destMoreThanSrcList.add(new Map[]{srcCbbTabColumnsItem, destCbbTabColumnsItem});
                    continue;
               }
               exceptionMap = new HashMap<>();
               // 比较字符类型的列
               for(String field: stringFields){
                   Object destFieldValue = destCbbTabColumnsItem.get(field);
                   Object srcFieldValue = srcCbbTabColumnsItem.get(field);
                   if(!StringUtils.defaultString((String) destFieldValue).equals(StringUtils.defaultString((String) srcFieldValue))){
                       if("COL_LABEL".equals(field) || "COL_IS_SYS".equals(field)){
                           overwriteColumnList.add(new Map[]{srcCbbTabColumnsItem, destCbbTabColumnsItem});
                       }else{
                           cbbTabExceptionColumnsDTOList.add(new CbbTabExceptionColumnsDTO(srcCbbTables.getId(), destCbbTables.getId(), (String) srcCbbTabColumnsItem.get("ID"), (String) destCbbTabColumnsItem.get("ID"), colName, field, (String)srcFieldValue, (String)destFieldValue));
                       }
                   }
               }
               // 比较数字类型的列
               for(String field: numberFields){
                   Object destFieldValue = destCbbTabColumnsItem.get(field);
                   Object srcFieldValue = srcCbbTabColumnsItem.get(field);

                   if(srcFieldValue == null && destFieldValue == null){
                       continue;
                   }

                   // 长度字段, 都不为空, 且源长度大于目标长度
                   if("COL_LENGTH".equals(field) && destFieldValue != null && srcFieldValue!=null &&  ((BigDecimal)srcFieldValue).compareTo((BigDecimal)destFieldValue)>0 ){ // 长度
                       colLengthDiffList.add(new Map[]{srcCbbTabColumnsItem, destCbbTabColumnsItem});
                       continue;
                   }

                   if(destFieldValue == null && srcFieldValue!=null){ // 目标值为空, 源值不为空时记录
                           cbbTabExceptionColumnsDTOList.add(
                                   new CbbTabExceptionColumnsDTO(srcCbbTables.getId(), destCbbTables.getId(),
                                   (String) srcCbbTabColumnsItem.get("ID"),  (String) destCbbTabColumnsItem.get("ID"),
                                   colName, field, srcFieldValue == null ? null : srcFieldValue.toString(), destFieldValue == null ? null : destFieldValue.toString()));
                   }else{ // 目标值不为空, 源值为空或和目标值不等时记录
                       if(srcFieldValue == null || ((BigDecimal)srcFieldValue).compareTo((BigDecimal)destFieldValue)!=0 ){
                           cbbTabExceptionColumnsDTOList.add(new CbbTabExceptionColumnsDTO(srcCbbTables.getId(), destCbbTables.getId(),
                                   (String) srcCbbTabColumnsItem.get("ID"),  (String) destCbbTabColumnsItem.get("ID"),
                                   colName, field, srcFieldValue == null ? null : srcFieldValue.toString(), destFieldValue == null ? null : destFieldValue.toString()));
                       }
                   }

               }
               if(exceptionMap.size()>0){
                   exceptionList.add(exceptionMap);
               }
               
            }
            for(String colName: srcCbbTabColumnsMap.keySet()){
                if(destCbbTabColumnsMap.get(colName) == null){// 源节点有而目标节点没有
                    srcMoreThanDestList.add(srcCbbTabColumnsMap.get(colName));
                }
            }
            //
            subOrgId = destCbbTables.getOrgId();
            // 保存新增的列, 同时为物理表增加字段
            saveCbbTabColumns(srcMoreThanDestList, (String) subOrgId, destCbbTables);
            // 处理目标表多出的列
            handleDestMoreThanSrcColumnList(destMoreThanSrcList, destCbbTables, srcCbbTables);
            // 覆盖
            overwriteColumn(overwriteColumnList, destCbbTables.getTableName());
            //处理长度列
            handleColLengthDiffList(colLengthDiffList, destCbbTables);

            // 处理差异列
            handleDiffList(cbbTabExceptionColumnsDTOList);

            syncInfo = new HashMap<>();
            syncInfo.put("srcList", srcCbbTabColumnsMap.values());
            syncInfo.put("destList", destCbbTabColumnsMap.values());
            syncInfo.put("srcMoreThanDestList", srcMoreThanDestList);
            syncInfo.put("destMoreThanSrcList", destMoreThanSrcList);
            syncInfo.put("exceptionList", cbbTabExceptionColumnsDTOList);
            result.put(subOrgId, syncInfo);
//            break; // TODO 提交时去掉
        }

        syncCbbTabCode(srcTableId, destCbbTablesList);
        return result;
    }

    private void overwriteColumn(List<Map<String, Object>[]> overwriteColumnList, String tableName) throws InvocationTargetException, IllegalAccessException {
        if(CollectionUtils.isEmpty(overwriteColumnList)){
            return;
        }
        Map<String, Object> srcCbbTabColumnsItem;
        Map<String, Object> destCbbTabColumnsItem;
        Map<String, Object> destCbbTabColumnsMap;
        CbbTabColumns destCbbTabColumns;
        List<CbbTabColumns> cbbTabColumnsList = new ArrayList<>(overwriteColumnList.size());
        List<Map<String, Object>> cbbTabColumnsMapList = new ArrayList<>(overwriteColumnList.size());
        for (Map<String, Object>[] item : overwriteColumnList) {
            srcCbbTabColumnsItem = item[0];
            destCbbTabColumnsItem = item[1];

            destCbbTabColumns = new CbbTabColumns();
            destCbbTabColumnsMap = new HashMap<>();
            for (String field : destCbbTabColumnsItem.keySet()) {
                destCbbTabColumnsMap.put(CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, field), destCbbTabColumnsItem.get(field));
            }
            BigDecimalConverter bd = new BigDecimalConverter(null);
            DateConverter dc = new DateConverter(null);
            ConvertUtils.register(bd, BigDecimal.class);
            ConvertUtils.register(dc, Date.class);
            BeanUtils.populate(destCbbTabColumns, destCbbTabColumnsMap);
            destCbbTabColumns.setColLabel((String) srcCbbTabColumnsItem.get("COL_LABEL"));
            destCbbTabColumns.setColIsSys((String) srcCbbTabColumnsItem.get("COL_IS_SYS"));

            cbbTabColumnsList.add(destCbbTabColumns);

            destCbbTabColumnsItem.put("COL_LABEL",  srcCbbTabColumnsItem.get("COL_LABEL"));
            destCbbTabColumnsItem.put("COL_IS_SYS", srcCbbTabColumnsItem.get("COL_IS_SYS"));
            cbbTabColumnsMapList.add(destCbbTabColumnsItem);
        }
        lantopArchiveClassDao2.updateCbbTabColumnsList(cbbTabColumnsMapList);
        lantopArchiveClassDao2.setFieldCommentToPhysicalTable(cbbTabColumnsList, tableName);
    }

    /**
     * 目标列中多出来的字段, 删除没有值的列,同时物理表中也去掉相应的列
     * @param destCbbTables
     * @param srcCbbTables
     */
    public void handleDestMoreThanSrcColumnList(List<Map<String, Object>[]> destMoreThanSrcColumnList, CbbTables destCbbTables, CbbTables srcCbbTables) throws Exception {
        if(CollectionUtils.isEmpty(destMoreThanSrcColumnList)){
            return;
        }
        String tableName = destCbbTables.getTableName();
        List<Map<String, Object>> emptyDataCbbTabColumnsList = new ArrayList<>();
        List<String> emptyIds = new ArrayList<>();
        List<CbbTabExceptionColumnsDTO> cbbTabExceptionColumnsDTOList = new ArrayList<>();

        Map<String, Object> srcCbbTabColumnsItem;
        Map<String, Object> destCbbTabColumnsItem;
        Map<String, Object> destCbbTabColumnsMap;
        CbbTabColumns destCbbTabColumns;
        List<CbbTabColumns> cbbTabColumnsList = new ArrayList<>(destMoreThanSrcColumnList.size());
        List<Map<String, Object>> cbbTabColumnsMapList = new ArrayList<>(destMoreThanSrcColumnList.size());
        for (Map<String, Object>[] item : destMoreThanSrcColumnList) {
            srcCbbTabColumnsItem = item[0];
            destCbbTabColumnsItem = item[1];

            boolean isEmpty = lantopArchiveClassDao2.isColumnDataEmpty(tableName, (String) destCbbTabColumnsItem.get("COL_NAME"), (String) destCbbTabColumnsItem.get("COL_TYPE"));
            if(isEmpty){
                emptyDataCbbTabColumnsList.add(destCbbTabColumnsItem);
                emptyIds.add((String) destCbbTabColumnsItem.get("ID"));
            }else{
                cbbTabExceptionColumnsDTOList.add(
                        new CbbTabExceptionColumnsDTO(srcCbbTables.getId(), destCbbTables.getId(),
                                (String) srcCbbTabColumnsItem.get("ID"),  (String)destCbbTabColumnsItem.get("ID"),
                                (String)destCbbTabColumnsItem.get("COL_NAME"), "COL_LENGTH",
                                srcCbbTabColumnsItem.get("COL_LENGTH") == null ? null: srcCbbTabColumnsItem.get("COL_LENGTH").toString(),
                                destCbbTabColumnsItem.get("COL_LENGTH") == null ? null: destCbbTabColumnsItem.get("COL_LENGTH").toString()));
            }
        }

        if(emptyIds.size()>0){
            lantopArchiveClassDao2.deleteCbbTabColumnsList(emptyIds);
            lantopArchiveClassDao2.deleteFieldInPhysicalTable(destCbbTables, emptyDataCbbTabColumnsList);
        }
        handleDiffList(cbbTabExceptionColumnsDTOList);
    }

    public void handleColLengthDiffList(List<Map<String, Object>[]> colLengthDiffList, CbbTables cbbTables){
        if(CollectionUtils.isEmpty(colLengthDiffList)){
            return;
        }
        Map<String, Object> srcCbbTabColumnsItem;
        Map<String, Object> destCbbTabColumnsItem;
        List<Map<String, Object>> cbbTabColumnsList = new ArrayList<>(colLengthDiffList.size());
        List<CbbTabColumns> cbbTabColumnsBeanList  = new ArrayList<>();
        CbbTabColumns cbbTabColumns;
        for (Map<String, Object>[] item : colLengthDiffList) {
            srcCbbTabColumnsItem = item[0];
            destCbbTabColumnsItem = item[1];
            destCbbTabColumnsItem.put("COL_LENGTH", srcCbbTabColumnsItem.get("COL_LENGTH"));
            cbbTabColumnsList.add(destCbbTabColumnsItem);

            cbbTabColumns = new CbbTabColumns();
            cbbTabColumns.setColName((String) destCbbTabColumnsItem.get("COL_NAME"));
            cbbTabColumns.setColType((String) destCbbTabColumnsItem.get("COL_TYPE"));
            cbbTabColumns.setColDecimal((BigDecimal) destCbbTabColumnsItem.get("COL_DECIMAL"));
            cbbTabColumns.setColLength((BigDecimal) destCbbTabColumnsItem.get("COL_LENGTH"));
            cbbTabColumns.setColLabel((String) destCbbTabColumnsItem.get("COL_LABEL"));
            cbbTabColumnsBeanList.add(cbbTabColumns);
        }
        lantopArchiveClassDao2.updateCbbTabColumnsList(cbbTabColumnsList);
        lantopArchiveClassDao2.modifyFieldInPhysicalTable(cbbTables, cbbTabColumnsBeanList);
    }

    public void handleDiffList(List<CbbTabExceptionColumnsDTO> diffList) throws Exception {
        if(diffList.size()>0) {
            cbbTabExceptionColumnsService.insertCbbTabExceptionColumnsList(diffList); //TODO 提交时去掉代码注释
        }
    }

    public void saveCbbTabColumns(List<Map<String,Object>> srcCbbTabColumnsList, String orgId, CbbTables destCbbTables) throws Exception {
        if(CollectionUtils.isEmpty(srcCbbTabColumnsList)){
            return;
        }
        CbbTabColumns destCbbTabColumns;
        String tableId = destCbbTables.getId();
        Map<String, Object> destCbbTabColumnMap;
        List<CbbTabColumns> destCbbTabColumnsList = new ArrayList<>();
        for (Map<String, Object> srcCbbTabColumns : srcCbbTabColumnsList) {
            destCbbTabColumnMap = new HashMap<>();
            // 大写下划线转驼峰
            for (String field : srcCbbTabColumns.keySet()) {
                destCbbTabColumnMap.put(CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, field), srcCbbTabColumns.get(field));
            }
            destCbbTabColumns = new CbbTabColumns();
            try {
                BigDecimalConverter bd = new BigDecimalConverter(null);
                DateConverter dc = new DateConverter(null);
                ConvertUtils.register(bd, BigDecimal.class);
                ConvertUtils.register(dc, Date.class);
                BeanUtils.populate(destCbbTabColumns, destCbbTabColumnMap);
            } catch (Exception e) {
                e.printStackTrace();
                return;
            }
            destCbbTabColumns.setId(ComUtil.getId());
            destCbbTabColumns.setOrgId(orgId);
            destCbbTabColumns.setTableId(tableId);
            destCbbTabColumnsList.add(destCbbTabColumns);
            lantopArchiveClassDao2.saveCbbTabColumns(destCbbTabColumns);
        }
        lantopArchiveClassDao2.addFieldToPhysicalTable(destCbbTables,destCbbTabColumnsList);
    }

    public Map<String, String> buildCbbTablesIdToFullPathMap(String nodeId, Map<String, String> nodeIdentityToFullPathMap){
        List<Map<String, Object>> list = lantopArchiveClassDao2.getCbbTablesAndParentArchiveClass(nodeId);
        Map<String,String> cbbTablesIdToFullPathMap = new LinkedHashMap<>();
        LantopArchiveClass lantopArchiveClass;
        String nodeIdentity;
        String fullPath;
        for (Map<String, Object> map : list) {
            lantopArchiveClass = new LantopArchiveClass();
            lantopArchiveClass.setId((String) map.get("ID"));
            lantopArchiveClass.setOrgId((String) map.get("ORG_ID"));
            lantopArchiveClass.setArchiveClassNodeCode((String) map.get("ARCHIVE_CLASS_NODE_CODE"));
            lantopArchiveClass.setArchiveClassNodeName((String) map.get("ARCHIVE_CLASS_NODE_NAME"));
            lantopArchiveClass.setArchiveType((String) map.get("ARCHIVE_TYPE"));
            lantopArchiveClass.setArchiveClassNodeType((String) map.get("ARCHIVE_CLASS_NODE_TYPE"));

            nodeIdentity = getNodeIdentity(lantopArchiveClass);
            fullPath = nodeIdentityToFullPathMap.get(nodeIdentity);

            cbbTablesIdToFullPathMap.put((String) map.get("SUB_ID"), fullPath);
        }

        return cbbTablesIdToFullPathMap;
    }
    /**
     * 深拷贝
     * @param src
     * @param <T>
     * @return
     */
    public <T> T deeptClone(T src){
        T dest = null;
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;
        try{
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(src);

            // 反序列化
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            dest = (T) ois.readObject();
        } catch (ClassNotFoundException | IOException e) {
            e.printStackTrace();
        }finally {
            try {
                ois.close();
                bis.close();
                oos.close();
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return dest;
    }

    public void syncCbbTabCode(String srcTableId, List<CbbTables> destCbbTablesList) throws Exception {
        Map<String, List<Map<String, Object>>> srcColNameCbbTabColCodeMap = lantopArchiveClassDao2.getColNameCbbTabColCodeMapByTableId(srcTableId);
        String destCbbTablesId;
        Map<String, String> destColNameColIdMap;
        List<Map<String, Object>> srcCbbTabColCodeList;
        Map<String, Object> destCbbTabColCodeMap;

        List<Map<String,Object>> destCbbTabColCodeList = new ArrayList<>();
        for (CbbTables destCbbTables : destCbbTablesList) {
            destCbbTablesId = destCbbTables.getId();
            destColNameColIdMap = lantopArchiveClassDao2.getColNameColIdMap(destCbbTablesId);

            for (String colName : srcColNameCbbTabColCodeMap.keySet()) {
                srcCbbTabColCodeList = srcColNameCbbTabColCodeMap.get(colName);
                if(destColNameColIdMap.get(colName)==null){
                    continue;
                }
                for (Map<String, Object> srcCbbTabColCodeMap : srcCbbTabColCodeList) {
                    destCbbTabColCodeMap = (Map<String, Object>) ((HashMap<String, Object>) srcCbbTabColCodeMap).clone();
                    destCbbTabColCodeMap.put("ID", ComUtil.getId());
                    destCbbTabColCodeMap.put("ORG_ID", destCbbTablesId);
                    destCbbTabColCodeMap.put("TABLE_ID", destCbbTablesId);
                    destCbbTabColCodeMap.put("COLUMN_ID", destColNameColIdMap.get(colName));
                    PojoUtil.setSysDyncProperties(destCbbTabColCodeMap, PlatformConstant.OpType.insert);

                    destCbbTabColCodeList.add(destCbbTabColCodeMap);
                }
            }
        }

        lantopArchiveClassDao2.saveCbbTabColCodeMapList(destCbbTabColCodeList);
    }

}
